package br.senai.sc.controller.business;

import java.math.BigDecimal;

import br.senai.sc.controller.exception.BusinessException;

/**
 * Contém os cálculos de IQA para o parâmetro DBO.
 * 
 * @author Tiago
 */
public final class CalculoDBOBusiness {

	/**
	 * Peso fixo utilizado para o cálculo da qualidade do DBO (qi^w)
	 */
	private static final double PESO = 0.10;

	/**
	 * Define o primeiro intervalo para o cálculo dos coliformes fecais. De 0 a
	 * menos que 30.
	 */
	public static final int[] PRIMEIRO_INTERVALO = { 0, 30 };

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * retorno do calculo de cada intervalo de valor.
	 */
	public static final int ESCALA = 10;

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * cálculo de valores exponensiais.
	 */
	private static final int ESCALA_EXP = 20;

	private CalculoDBOBusiness() {

	}

	/**
	 * Utilizado para fazer o cálculo do DBO caso o valor informado é maior ou
	 * igual a 0 e menor que 30.
	 * 
	 * Cálculo realizado: -2,34099938989894E-06*B12^6 +
	 * 0,00022214828759981*B12^5 - 0,00784791328915802*B12^4 +
	 * 0,120683917172016*B12^3 - 0,509524519829029*B12^2 - 7,58366372497312*B12
	 * + 97,6255511774702;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal primeiroIntervalo(BigDecimal valor)
			throws BusinessException {

		if ((valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[0])) == -1)
				|| (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) == 1)) {
			throw new BusinessException();
		}

		BigDecimal valorExp6 = new BigDecimal(Math.pow(valor.doubleValue(), 6))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp5 = new BigDecimal(Math.pow(valor.doubleValue(), 5))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp4 = new BigDecimal(Math.pow(valor.doubleValue(), 4))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp3 = new BigDecimal(Math.pow(valor.doubleValue(), 3))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// -2,34099938989894E-06*B12^6 + 0,00022214828759981*B12^5 -
		// 0,00784791328915802*B12^4 + 0,120683917172016*B12^3 -
		// 0,509524519829029*B12^2 - 7,58366372497312*B12 + 97,6255511774702;
		BigDecimal retorno = new BigDecimal("2.34099938989894E-06")
				.multiply(valorExp6).negate() // -2,34099938989894E-06*B12^6
				.add(new BigDecimal("0.00022214828759981").multiply(valorExp5))
				// + 0,00022214828759981*B12^5
				.subtract(
						new BigDecimal("0.00784791328915802")
								.multiply(valorExp4))
				// - 0,00784791328915802*B12^4
				.add(new BigDecimal("0.120683917172016").multiply(valorExp3))
				// + 0,120683917172016*B12^3
				.subtract(
						new BigDecimal("0.509524519829029").multiply(valorExp2))
				// - 0,509524519829029*B12^2
				.subtract(new BigDecimal("7.58366372497312").multiply(valor))
				// - 7,58366372497312*B12
				.add(new BigDecimal("97.6255511774702")); // + 97.6255511774702

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Utilizado para fazer o cálculo do DBO caso o valor seja igual a 30
	 * 
	 * Cálculo realizado: -0,00000234099938989894*valor^6 +
	 * 0,00022214828759981*valor^5 - 0,00784791328915802*valor^4 +
	 * 0,120683917172016*valor^3 - 0,509524519829029*valor^2 -
	 * 7,58366372497312*valor + 97,6255511774702;
	 * 
	 * @param valor
	 * @return
	 */
	private static BigDecimal igualAFimPrimeiroIntervalo(BigDecimal valor)
			throws BusinessException {

		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) != 0) {
			throw new BusinessException();
		}

		BigDecimal valorExp6 = new BigDecimal(Math.pow(valor.doubleValue(), 6))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp5 = new BigDecimal(Math.pow(valor.doubleValue(), 5))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp4 = new BigDecimal(Math.pow(valor.doubleValue(), 4))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp3 = new BigDecimal(Math.pow(valor.doubleValue(), 3))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);
		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// -0,00000234099938989894*valor^6 + 0,00022214828759981*valor^5 -
		// 0,00784791328915802*valor^4 + 0,120683917172016*valor^3
		// -0,509524519829029*valor^2 - 7,58366372497312*valor +
		// 97,6255511774702;
		BigDecimal retorno = new BigDecimal("0.00000234099938989894")
				.multiply(valorExp6).negate() // -0,00000234099938989894*valor^6
				.add(new BigDecimal("0.00022214828759981").multiply(valorExp5))
				// 0,00022214828759981*valor^5
				.subtract(
						new BigDecimal("0.00784791328915802")
								.multiply(valorExp4))
				// - 0,00784791328915802*valor^4
				.add(new BigDecimal("0.120683917172016").multiply(valorExp3))
				// + 0,120683917172016*valor^3
				.subtract(
						new BigDecimal("0.509524519829029").multiply(valorExp2))
				// -0,509524519829029*valor^2
				.subtract(new BigDecimal("7.58366372497312").multiply(valor))
				// - 7,58366372497312*valor
				.add(new BigDecimal("97.6255511774702")); // + 97,6255511774702
		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);

	}

	/**
	 * Calcula o IQA do OD informado. Cálculo realizado: Se [valor] < que 0
	 * Lança exceção Se [valor] = 0 IQA = 0 Se [valor] <= 30
	 * 
	 * @see {@link CalculoDBOBusiness#primeiroIntervalo(double)}
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido (Ex: menor que
	 *             0).
	 */
	public static BigDecimal calculaIQA(BigDecimal valor)
			throws BusinessException {

		// se o valor for menor que 0 o mesmo está incorreto
		if (valor.compareTo(BigDecimal.ZERO) < 0) {
			throw new BusinessException();
		}

		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) < 0) {
			return primeiroIntervalo(valor);
		}

		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) == 0) {
			return igualAFimPrimeiroIntervalo(valor);
		}
		// se o valor for maior que 30 retorna 2
		int retornoValorMaiorQueIntervalos = 2;
		return new BigDecimal(retornoValorMaiorQueIntervalos);
	}

	/**
	 * Calcula a média de qualidade (qi^w) do DBO.
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido.
	 */
	protected static BigDecimal mediaQualidade(BigDecimal valor)
			throws BusinessException {
		return new BigDecimal(Math.pow(calculaIQA(valor).doubleValue(), PESO));
	}

}
